home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Ebooks / Thinking in Java / c17 / FieldOBeasts.java < prev   
Encoding:
Java Source  |  2000-05-25  |  10.4 KB  |  322 lines

  1. //: FieldOBeasts.java
  2. //////////////////////////////////////////////////
  3. // Copyright (c) Bruce Eckel, 1998
  4. // Source code file from the book "Thinking in Java"
  5. // All rights reserved EXCEPT as allowed by the
  6. // following statements: You can freely use this file
  7. // for your own work (personal or commercial),
  8. // including modifications and distribution in
  9. // executable form only. Permission is granted to use
  10. // this file in classroom situations, including its
  11. // use in presentation materials, as long as the book
  12. // "Thinking in Java" is cited as the source. 
  13. // Except in classroom situations, you cannot copy
  14. // and distribute this code; instead, the sole
  15. // distribution point is http://www.BruceEckel.com 
  16. // (and official mirror sites) where it is
  17. // freely available. You cannot remove this
  18. // copyright and notice. You cannot distribute
  19. // modified versions of the source code in this
  20. // package. You cannot use this file in printed
  21. // media without the express permission of the
  22. // author. Bruce Eckel makes no representation about
  23. // the suitability of this software for any purpose.
  24. // It is provided "as is" without express or implied
  25. // warranty of any kind, including any implied
  26. // warranty of merchantability, fitness for a
  27. // particular purpose or non-infringement. The entire
  28. // risk as to the quality and performance of the
  29. // software is with you. Bruce Eckel and the
  30. // publisher shall not be liable for any damages
  31. // suffered by you or any third party as a result of
  32. // using or distributing software. In no event will
  33. // Bruce Eckel or the publisher be liable for any
  34. // lost revenue, profit, or data, or for direct,
  35. // indirect, special, consequential, incidental, or
  36. // punitive damages, however caused and regardless of
  37. // the theory of liability, arising out of the use of
  38. // or inability to use software, even if Bruce Eckel
  39. // and the publisher have been advised of the
  40. // possibility of such damages. Should the software
  41. // prove defective, you assume the cost of all
  42. // necessary servicing, repair, or correction. If you
  43. // think you've found an error, please email all
  44. // modified files with clearly commented changes to:
  45. // Bruce@EckelObjects.com. (Please use the same
  46. // address for non-code errors found in the book.)
  47. /////////////////////////////////////////////////
  48.  
  49. // Demonstration of complexity theory; simulates 
  50. // herding behavior in animals. Adapted from
  51. // a program by Larry O'Brien lobrien@msn.com
  52. import java.awt.*;
  53. import java.awt.event.*;
  54. import java.applet.*;
  55. import java.util.*;
  56.  
  57. class Beast {
  58.   int
  59.     x, y,            // Screen position
  60.     currentSpeed;    // Pixels per second
  61.   float currentDirection;  // Radians
  62.   Color color;      // Fill color
  63.   FieldOBeasts field; // Where the Beast roams
  64.   static final int GSIZE = 10; // Graphic size
  65.  
  66.   public Beast(FieldOBeasts f, int x, int y, 
  67.       float cD, int cS, Color c) {
  68.     field = f;
  69.     this.x = x;
  70.     this.y = y;
  71.     currentDirection = cD;
  72.     currentSpeed = cS;
  73.     color = c;
  74.   }
  75.   public void step() {
  76.     // You move based on those within your sight:
  77.     Vector seen = field.beastListInSector(this);
  78.     // If you're not out in front
  79.     if(seen.size() > 0) {
  80.       // Gather data on those you see
  81.       int totalSpeed = 0;
  82.       float totalBearing = 0.0f;
  83.       float distanceToNearest = 100000.0f;
  84.       Beast nearestBeast = 
  85.         (Beast)seen.elementAt(0);
  86.       Enumeration e = seen.elements();
  87.       while(e.hasMoreElements()) {
  88.         Beast aBeast = (Beast) e.nextElement();
  89.         totalSpeed += aBeast.currentSpeed;
  90.         float bearing = 
  91.           aBeast.bearingFromPointAlongAxis(
  92.             x, y, currentDirection);
  93.         totalBearing += bearing;
  94.         float distanceToBeast = 
  95.           aBeast.distanceFromPoint(x, y);
  96.         if(distanceToBeast < distanceToNearest) {
  97.           nearestBeast = aBeast;
  98.           distanceToNearest = distanceToBeast;
  99.         }
  100.       }
  101.       // Rule 1: Match average speed of those 
  102.       // in the list:
  103.       currentSpeed = totalSpeed / seen.size();
  104.       // Rule 2: Move towards the perceived
  105.       // center of gravity of the herd:
  106.       currentDirection = 
  107.         totalBearing / seen.size();
  108.       // Rule 3: Maintain a minimum distance 
  109.       // from those around you:
  110.       if(distanceToNearest <= 
  111.          field.minimumDistance) {
  112.         currentDirection = 
  113.           nearestBeast.currentDirection;
  114.         currentSpeed = nearestBeast.currentSpeed;
  115.         if(currentSpeed > field.maxSpeed) {
  116.           currentSpeed = field.maxSpeed;
  117.         }
  118.       }
  119.     } 
  120.     else {  // You are in front, so slow down
  121.       currentSpeed = 
  122.         (int)(currentSpeed * field.decayRate);
  123.     }
  124.     // Make the beast move:
  125.     x += (int)(Math.cos(currentDirection) 
  126.                * currentSpeed);
  127.     y += (int)(Math.sin(currentDirection)
  128.                * currentSpeed);
  129.     x %= field.xExtent;
  130.     y %= field.yExtent;
  131.     if(x < 0)
  132.       x += field.xExtent;
  133.     if(y < 0)
  134.       y += field.yExtent;
  135.   }
  136.   public float bearingFromPointAlongAxis (
  137.       int originX, int originY, float axis) {
  138.     // Returns bearing angle of the current Beast
  139.     // in the world coordiante system
  140.     try {
  141.       double bearingInRadians = 
  142.         Math.atan(
  143.           (this.y - originY) / 
  144.           (this.x - originX));
  145.       // Inverse tan has two solutions, so you 
  146.       // have to correct for other quarters:
  147.       if(x < originX) {  
  148.         if(y < originY) {
  149.           bearingInRadians += - (float)Math.PI;
  150.         } 
  151.         else {
  152.           bearingInRadians = 
  153.             (float)Math.PI - bearingInRadians;
  154.         }
  155.       }
  156.       // Just subtract the axis (in radians):
  157.       return (float) (axis - bearingInRadians);
  158.     } catch(ArithmeticException aE) {
  159.       // Divide by 0 error possible on this
  160.       if(x > originX) {
  161.           return 0;
  162.       } 
  163.       else
  164.         return (float) Math.PI;
  165.     }
  166.   }
  167.   public float distanceFromPoint(int x1, int y1){
  168.     return (float) Math.sqrt(
  169.       Math.pow(x1 - x, 2) + 
  170.       Math.pow(y1 - y, 2));
  171.   }
  172.   public Point position() { 
  173.     return new Point(x, y);
  174.   }
  175.   // Beasts know how to draw themselves:
  176.   public void draw(Graphics g) {
  177.     g.setColor(color);
  178.     int directionInDegrees = (int)(
  179.       (currentDirection * 360) / (2 * Math.PI));
  180.     int startAngle = directionInDegrees - 
  181.       FieldOBeasts.halfFieldOfView;
  182.     int endAngle = 90;
  183.     g.fillArc(x, y, GSIZE, GSIZE, 
  184.       startAngle, endAngle);
  185.   }
  186. }
  187.  
  188. public class FieldOBeasts extends Applet 
  189.     implements Runnable {
  190.   private Vector beasts;
  191.   static float 
  192.     fieldOfView = 
  193.       (float) (Math.PI / 4), // In radians
  194.     // Deceleration % per second:
  195.     decayRate = 1.0f, 
  196.     minimumDistance = 10f; // In pixels
  197.   static int
  198.     halfFieldOfView = (int)(
  199.       (fieldOfView * 360) / (2 * Math.PI)),
  200.     xExtent = 0,
  201.     yExtent = 0,
  202.     numBeasts = 50,
  203.     maxSpeed = 20; // Pixels/second
  204.   boolean uniqueColors = true;
  205.   Thread thisThread;
  206.   int delay = 25;
  207.   public void init() {
  208.     if (xExtent == 0 && yExtent == 0) {
  209.       xExtent = Integer.parseInt(
  210.         getParameter("xExtent"));
  211.       yExtent = Integer.parseInt(
  212.         getParameter("yExtent"));
  213.     }
  214.     beasts = 
  215.       makeBeastVector(numBeasts, uniqueColors);
  216.     // Now start the beasts a-rovin':
  217.     thisThread = new Thread(this);
  218.     thisThread.start();
  219.   }
  220.   public void run() {
  221.     while(true) {
  222.       for(int i = 0; i < beasts.size(); i++){
  223.         Beast b = (Beast) beasts.elementAt(i);
  224.         b.step();
  225.       }
  226.       try {
  227.         thisThread.sleep(delay);
  228.       } catch(InterruptedException ex){}
  229.       repaint(); // Otherwise it won't update
  230.     }
  231.   }
  232.   Vector makeBeastVector(
  233.       int quantity, boolean uniqueColors) {
  234.     Vector newBeasts = new Vector();
  235.     Random generator = new Random();
  236.     // Used only if uniqueColors is on:
  237.     double cubeRootOfBeastNumber = 
  238.       Math.pow((double)numBeasts, 1.0 / 3.0);
  239.     float colorCubeStepSize = 
  240.       (float) (1.0 / cubeRootOfBeastNumber);
  241.     float r = 0.0f;
  242.     float g = 0.0f;
  243.     float b = 0.0f;
  244.     for(int i = 0; i < quantity; i++) {
  245.       int x = 
  246.         (int) (generator.nextFloat() * xExtent);
  247.       if(x > xExtent - Beast.GSIZE) 
  248.         x -= Beast.GSIZE;
  249.       int y = 
  250.         (int) (generator.nextFloat() * yExtent);
  251.       if(y > yExtent - Beast.GSIZE) 
  252.         y -= Beast.GSIZE;
  253.       float direction = (float)(
  254.         generator.nextFloat() * 2 * Math.PI);
  255.       int speed = (int)(
  256.         generator.nextFloat() * (float)maxSpeed);
  257.       if(uniqueColors) {
  258.         r += colorCubeStepSize;
  259.         if(r > 1.0) {
  260.           r -= 1.0f;
  261.           g += colorCubeStepSize;
  262.           if( g > 1.0) {
  263.             g -= 1.0f;
  264.             b += colorCubeStepSize;
  265.             if(b > 1.0) 
  266.               b -= 1.0f;
  267.           }
  268.         }
  269.       }
  270.       newBeasts.addElement(
  271.         new Beast(this, x, y, direction, speed, 
  272.           new Color(r,g,b)));
  273.     }
  274.     return newBeasts;
  275.   }
  276.   public Vector beastListInSector(Beast viewer) {
  277.     Vector output = new Vector();
  278.     Enumeration e = beasts.elements();
  279.     Beast aBeast = (Beast)beasts.elementAt(0);
  280.     int counter = 0;
  281.     while(e.hasMoreElements()) {
  282.       aBeast = (Beast) e.nextElement();
  283.       if(aBeast != viewer) {
  284.         Point p = aBeast.position();
  285.         Point v = viewer.position();
  286.         float bearing = 
  287.           aBeast.bearingFromPointAlongAxis(
  288.             v.x, v.y, viewer.currentDirection);
  289.         if(Math.abs(bearing) < fieldOfView / 2)
  290.          output.addElement(aBeast);
  291.       }
  292.     }
  293.     return output;
  294.   }
  295.   public void paint(Graphics g)  {
  296.     Enumeration e = beasts.elements();
  297.     while(e.hasMoreElements()) {
  298.       ((Beast)e.nextElement()).draw(g);
  299.     }
  300.   }
  301.   public static void main(String[] args)   {
  302.     FieldOBeasts field = new FieldOBeasts();
  303.     field.xExtent = 640;
  304.     field.yExtent = 480;
  305.     Frame frame = new Frame("Field 'O Beasts");
  306.     // Optionally use a command-line argument
  307.     // for the sleep time:
  308.     if(args.length >= 1)
  309.       field.delay = Integer.parseInt(args[0]);
  310.     frame.addWindowListener(
  311.       new WindowAdapter() {
  312.         public void windowClosing(WindowEvent e) {
  313.           System.exit(0);
  314.         }
  315.       });
  316.     frame.add(field, BorderLayout.CENTER);
  317.     frame.setSize(640,480);
  318.     field.init();
  319.     field.start();
  320.     frame.setVisible(true);
  321.   }
  322. } ///:~